package org.diy.traffic.sources;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
import java.util.logging.Level;

import org.apache.commons.httpclient.HttpStatus;
import org.diy.traffic.daemon.AirTrafficController;
import org.diy.traffic.sinks.TrafficSink.TrafficSinkException;
import org.diy.traffic.sources.TrafficSource.TrafficSourceException;
import org.diy.traffic.updates.TrafficUpdate;
import org.diy.traffic.util.TLog;

import winterwell.jtwitter.TwitterException;
import winterwell.jtwitter.Twitter.Status;

public class DiyFluSource extends FeedSource {

	private static String sourceType = "DIYFLU_SOURCE";

	private static final int DEFAULT_GO_DAYS_BACK = 45;
	
	private static String mysqlDbName;
	private static String mysqlUsername;
	private static String mysqlPassword;
	private static int goDaysBack;
	
	private boolean UPDATE_IN_PROGRESS = false;
	
	// this should really be parameterized!
	private static int SOURCE_UPDATE_INTERVAL  = 600; // should be ten minites == 600s
	
	// this is the default; it is updated via the udpateInterval config parameter
	private static int FNCTL_PER_CITY_UPDATE_S = 3600; // each city updates every hour
	
	private Connection dbConn = null;
	
	private int lastCheckedCityId = -1;
	
	public DiyFluSource() throws TrafficSourceException {
		//this(TrafficSource.DEFAULT_UPDATE_FREQUENCY_S);
		this(SOURCE_UPDATE_INTERVAL); // every ten minutes is when inputdaemon tells me to update....
	}

	public DiyFluSource(int updateInterval) throws TrafficSourceException {
		super(sourceType, "http://search.twitter.com/search.json?", updateInterval);
		//this.sourceName = sourceType + "_" + username;
		initDB();
		
		// do this so that last check isn't 0, so that it won't think that all the cities are new.... 
		//this.setLastCheckToNow();
		this.setLastCheck( System.currentTimeMillis() - (1000*(updateInterval+2)) );
	}
	
	public void initDB() throws TrafficSourceException {
		try {
			dbConn = DriverManager.getConnection(
					String.format("jdbc:mysql://localhost/%s?user=%s&password=%s", mysqlDbName, mysqlUsername, mysqlPassword)
					);
		} catch (SQLException e) {
			TLog.log(Level.SEVERE, e);
			throw new TrafficSourceException(String.format("Can't connect to the database with dbname: '%s', username: '%s', password: '%s'",
					mysqlDbName, mysqlUsername, mysqlPassword));

		}
	}

	public static DiyFluSource createFromConfigData(HashMap<String, String> configParams) throws TrafficSourceException {
		mysqlDbName   = configParams.get("db");
		mysqlUsername = configParams.get("username");
		mysqlPassword = configParams.get("password");
		try {
			goDaysBack    = Integer.parseInt(configParams.get("history"));
		} catch (Exception e) {
			goDaysBack = DEFAULT_GO_DAYS_BACK;
		}
			
		
	
		if ((mysqlDbName == null) || (mysqlUsername == null) || (mysqlPassword == null)) {
			TLog.log(Level.SEVERE, "DiyFluSource requires a 'db', 'username', and 'password' parameter for configuration! None provided.");
			return null;
		}
		
		String updateInterval = configParams.get("updateInterval");
		if (updateInterval != null) {
			FNCTL_PER_CITY_UPDATE_S = Integer.parseInt(updateInterval);
		}
		
		return new DiyFluSource();
	}
	
	
	@Override
	public List<TrafficUpdate> doCheck(boolean updateLastCheck)
			throws TrafficSourceException {

		/** add this back in when threads are working...
		this.UPDATE_IN_PROGRESS = true;
		**/
		
		/**
		 * Description of Algorithm
		 * per_city_update_interval = the interval on which any given city should be updated
		 * source_update_interval   = the interval on which diyflusource updates
		 * 
		 * i.e. there are 20 cities (num_cities=20)[GIVEN]
		 * you want each one to update every 60 minutes (per_city_update=60m) [VARIABLE]
		 * you want the source_update_interval to run every 10 minutes (source_update=10m)[VARIABLE]
		 * 
		 * per_city_update / source_update == number of times it will run in the per_city_update interval (num_runs)
		 * num_cities / num_runs == the number of cities per run to achieve the per_city_update (recalc each round based on num_cities)
		 */
		
		List<TrafficUpdate> update_list = new ArrayList<TrafficUpdate>();
		
		Date last_check_date = new Date(this.getLastCheck());
		String last_check_str = getDateInMySQLFormat(last_check_date);
		
		ResultSet next_n_rs = null;
		Statement next_n_stmt = null;
		
		ResultSet since_rs = null;
		Statement since_stmt = null;
		
		try {
			
			Statement countStmt = dbConn.createStatement();
			ResultSet countRs   = countStmt.executeQuery("SELECT COUNT(*) FROM cities");
			countRs.first();
			int cityCount = countRs.getInt(1);
			
			
			countRs.close();
			countStmt.close();
			
			//System.err.println("number of citeis is " + cityCount);
			
			// interval specified in config file on which each city should be updated
			int CITY_UPDATE_S = (FNCTL_PER_CITY_UPDATE_S); 
			//System.err.println("my intended update frequency is " + CITY_UPDATE_S + " seconds.");
			
			// this TrafficSource updates every 600s = 10minutes
			int SOURCE_UPDATE_S      = SOURCE_UPDATE_INTERVAL; 
			//System.err.println("this source updates every " + SOURCE_UPDATE_S + " seconds");
			
			// for example, if you want each city to update every hour, and this source will be checked
			// every ten minutes, then this source will be checked 6 times within that interval...
			double NUM_CHECKS = CITY_UPDATE_S / SOURCE_UPDATE_S;
			//System.err.println("This source will be checked " + NUM_CHECKS + " time within the intended ferquency");
			
			// ake this a MEMBER FIELD and set it by default to 5
			// ... therefore, you must check 1/6th of the cities with each run!
			int NUM_CITIES_PER_CHECK = (int) Math.ceil(cityCount / NUM_CHECKS);
			TLog.log(Level.FINE, 
					String.format("Will check %d of %d cities this update", NUM_CITIES_PER_CHECK , cityCount)
					);
			
			
			next_n_stmt = dbConn.createStatement();
	    	
			next_n_rs = next_n_stmt.executeQuery(
					String.format("SELECT * FROM cities WHERE id>=%d ORDER BY id LIMIT %d",
							lastCheckedCityId+1, NUM_CITIES_PER_CHECK )
					);
			
			// if there are no results in the result set!
			if (!next_n_rs.last()) {
				lastCheckedCityId=-1;
				next_n_rs = next_n_stmt.executeQuery(
						String.format("SELECT * FROM cities WHERE id>%d ORDER BY id LIMIT %d",
								lastCheckedCityId, NUM_CITIES_PER_CHECK )
						);
			} else {
				next_n_rs.beforeFirst();
			}
			

			// need to put this here before all the work is done (which could take a while), to avoid creating a race condition....
			if (updateLastCheck) 
				this.setLastCheckToNow();
			
			boolean has_next = next_n_rs.next();
			
			// iterating through all the cities being checked for this check!
			while (has_next){
				String city_name = next_n_rs.getString("name");
				String country_name = next_n_rs.getString("country");
				String created_at  = next_n_rs.getString("created_at");
				String updated_at = next_n_rs.getString("updated_at");
				long city_id = next_n_rs.getLong("id");
				
				String[] lat_long;
				try {
					lat_long = this.fetchLatLongForCity(city_name);
				} catch (TrafficSourceException tse) {
					TLog.log(Level.WARNING, tse);
					has_next = next_n_rs.next();
					continue;
				}
				
				
				SimpleDateFormat date_formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
				Date created_at_date;
				try {
					created_at_date = date_formatter.parse(created_at);
				} catch (ParseException e) {
					TLog.log(Level.WARNING, e);
					has_next = next_n_rs.next();
					continue;
				}
				
				City a_city = new City(city_name, country_name, city_id, lat_long[0], lat_long[1]); 
				
				
				//TODO for each city, see what languages it uses, then go through this whole thing once for
				// each language. fetchAllPhrasesForCity(id)
				// get all the phrases, group by language, break this up into sets and issue one query for each with the 'lang' param
				// maybe each Phrase needs a 'lang' field?
				GoGetter gg = new GoGetter(this, a_city, fetchAllPhrases());
				
				// if the last time you checked was before this city was added ==
				// the city is new
				//if (last_check_date.compareTo(created_at_date) <= 0) {
				if (created_at.compareTo(updated_at) == 0) {
					TLog.log(Level.INFO, String.format("****Found new city: '%s', getting %s days of historical data.", city_name, goDaysBack));
					
					/**
					Thread t = new Thread(gg);
					t.start();
					**/
					
					update_list.addAll(gg.getPreviousDays(goDaysBack));
					
				} else {
					TLog.log(Level.FINE, String.format("Updating recent mention of all terms for city '%s'", city_name));
					
					since_stmt = dbConn.createStatement();
					since_rs   = since_stmt.executeQuery(
							String.format("select MAX(source_id) FROM mentions WHERE city_id=%d", (int)city_id) 
							);
					
					since_rs.first();
					String res = since_rs.getString("MAX(source_id)");
					//if (res.compareTo("NULL")==0) {
					if (res == null) {
						TLog.log(Level.FINER, "(Fetching all of today's)");
						update_list.addAll(gg.getToday());
					} else {
						int since_id = Integer.parseInt(res);
						TLog.log(Level.FINER, String.format("(Fetching only since id %d)", since_id));
						update_list.addAll(gg.getSinceId(since_id));
					}
					since_rs.close();
					since_stmt.close();
					
				}
				TLog.log(Level.FINE, String.format("Completed update for city '%s'", city_name));
				
				lastCheckedCityId = (int) city_id;
				Statement update_stmt = dbConn.createStatement();
				int update_result = update_stmt.executeUpdate("UPDATE cities SET updated_at=NOW() WHERE id=" + city_id);
				//System.err.println("lastCheckedCityId is now " + lastCheckedCityId);
				
				update_stmt.close();
				
				has_next = next_n_rs.next();
			}
			
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			TLog.log(Level.WARNING, e);
			throw new TrafficSourceException("DiyFluSource: MySQL error occurred during check");
		} finally {
			try {
				if (next_n_rs != null) next_n_rs.close();
				if (next_n_stmt != null) next_n_stmt.close();
				if (since_rs != null) since_rs.close();
				if (since_stmt != null) since_stmt.close();
				
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				TLog.log(Level.WARNING, e);
			}
			
		}
		
		/** add this back in when threads are working 
		catch (TrafficSourceException te) {
			throw te;
		} finally {
			// is this safe here?!?
			this.UPDATE_IN_PROGRESS = false;
		}
		**/
		
		System.gc();
		return update_list;
	}

	private String[] fetchLatLongForCity(String cityName) throws TrafficSourceException {
		String coord[] = new String[2];
		//String latitude, longitude;
		
		ResultSet lat_long_rs = null; 
		Statement coord_stmt  = null;
		try {
			coord_stmt = dbConn.createStatement();

			lat_long_rs = coord_stmt.executeQuery(
					String.format("SELECT latitude, longitude FROM locations WHERE name=\"%s\"", cityName) 
					);
			
			
	
			lat_long_rs.last();
			if (lat_long_rs.getRow()<=0) {
				throw new TrafficSourceException("Can't find lat/long for city '" + cityName + "'" );
			}

			lat_long_rs.first();
			coord[0] = lat_long_rs.getString("latitude");
			coord[1] = lat_long_rs.getString("longitude");
			
			return coord;
			
		} catch (SQLException e) {
			TLog.log(Level.WARNING, e);
			throw new TrafficSourceException("SQL Error while fetching lat / longitude for '" +cityName+ "'");
		} finally {
			try {
				if (lat_long_rs != null) lat_long_rs.close();
				if (coord_stmt != null) coord_stmt.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				TLog.log(Level.WARNING, e);
			}
			
		}
	
		
	}
	
	private Phrase[] fetchAllPhrases() throws TrafficSourceException {
		Phrase[] phrase_array = null;
		
		try {
			Statement phrase_stmt;
			phrase_stmt = dbConn.createStatement();
			ResultSet phrase_rs  = phrase_stmt.executeQuery("Select id,title FROM phrases");	
			
			
			phrase_rs.last();
			
			int num_rows = phrase_rs.getRow();
			//System.out.println("NUMBER OF ROWS IS: " + num_rows);
			
			phrase_array = new Phrase[num_rows];
			
			phrase_rs.beforeFirst();
			
			while(phrase_rs.next()) {
				String phrase_title = phrase_rs.getString("title");
				long phrase_id      = phrase_rs.getLong("id");
			
				phrase_array[phrase_rs.getRow()-1] = new Phrase(phrase_title, phrase_id);
			}
			
			phrase_rs.close();
			phrase_stmt.close();
			
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			TLog.log(Level.WARNING, e);
			throw new TrafficSourceException("MySQL error occurred while fetching phrases");
		}
		
		return phrase_array;
	}
	
	
	public String getResultForParams(HashMap<String,String> params, int page) throws FeedSourceException {
		
		InputStream istr = this.getFeedAsStream(params);
		
		// or InputStream sis  = new FileInputStream("ABC.txt");
		byte[] buffer = new byte[200000];
		ByteArrayOutputStream baos = new ByteArrayOutputStream(200000);
		int bytesread = 0;
		            
		while(true)
		{
		        try {
					bytesread = istr.read(buffer);
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
		        if (bytesread == -1)
		             break;
		        baos.write(buffer, 0, bytesread);
		}
		
		byte[] stream_contents = baos.toByteArray();
		String result = new String(stream_contents);
		return result;
	}
	
	
	private static String getDateInTwitterFormat(Date dt) {
		return getDateInFormat(dt, "yyyy-MM-dd");
	}
	
	// does this  need to use 24 hours?!?
	private static String getDateInMySQLFormat(Date dt) {
		return getDateInFormat(dt, "yyyy-MM-dd HH:mm:ss");
	}
	
	private static String getDateInFormat(Date dt, String format) {
		
		SimpleDateFormat formatter = new SimpleDateFormat(format, 
				Locale.getDefault());
				
		return formatter.format(dt);
	}
	
	/**
	 * Encodes a String by substituting a "+" in place of " "
	 * @param val a string to be plus-encoded
	 * @return a string which represents val, with all " " characters replaced by "+" characters
	 */
	private String plusEncode(String val){
		return val.replace(" ", "+");
	}
	
	private class City {
		public String cityName;
		public String countryName;
		public long   cityId;
		public String longitude;
		public String latitude;
		
		public City(String name, String country, long id, String lat, String lng) {
			this.cityName = name;
			this.countryName = country;
			this.cityId = id;
			this.latitude = lat;
			this.longitude = lng;
		}
	}
	
	private class Phrase {
		public String text;
		public long phraseId;
		
		public Phrase(String txt, long id ) {
			this.text = txt;
			this.phraseId = id;
		}
	}
	
	private class Bounds {
		
		public final static String BOUND_TYPE_DATE = "DATE";
		public final static String BOUND_TYPE_ID = "ID";
		
		public HashMap<String,String> boundsParams;
		private String boundType;
		
		
		
		public Bounds() {
			this.boundsParams = new HashMap<String, String>();
		}
		
		public void addDateBounds(String since_date_str, String until_date_str) {
			this.boundType = Bounds.BOUND_TYPE_DATE;
			this.boundsParams.put("since", since_date_str);
			this.boundsParams.put("until", until_date_str);
		}
		
		public void addIdBounds(int id) {
			this.boundType = Bounds.BOUND_TYPE_ID;
			this.boundsParams.put("since_id", String.valueOf(id));
		}
	}
	
	// need to break out GoGetter into its own class.... with a generic method called "getWithParameters"
	// which takes a HashMap of params. Then create a getAllForCity(since id). Then access this from
	// another Source to get all tweets for a city. 
	// TODO But how to return both the number of tweets as well as id of the most recent tweet? (!!)
	// 		ANSWER: if you do return a list of TrafficUpdates, you can get both these pieces of information...
	// TODO Generic flag for intermediateUpdate (vs. returning a list)
	private class GoGetter implements Runnable {

		private City myCity;
		private TrafficSource tSource;
		private Phrase[] myPhrases;
		
		
		public GoGetter(TrafficSource source, City a_city, Phrase[] phrase_array) {
			this.tSource= source;
			this.myCity = a_city;
			this.myPhrases = phrase_array;
		}
		
		public List<TrafficUpdate> getPreviousDays(int previousDays) {
			return getPhrasesForCityForDays(this.myCity, this.myPhrases, previousDays);
		}
		
		public List<TrafficUpdate> getToday() {
			return getPhrasesForCityForDays(this.myCity, this.myPhrases, 1);
		}
		
		public List<TrafficUpdate> getSinceId(int since_id) {
			Bounds b = new Bounds();
			b.addIdBounds(since_id);
			return  getPhrasesForCityWithBounds(this.myCity, this.myPhrases, b);
		}


		/**
		 * Gets all the instances of a phrase in a city on a particular date
		 * @param cty A City wrapper object
		 * @param phr A Phrase wrapper object
		 * @param date_str A string representing the date on which to search
		 * @return
		 */
		
		
		/**
		 *  The atomic operation - one phrase for one city.
		 */
		public List<TrafficUpdate> getPhrasesForCityWithBounds(City cty, Phrase[] phr_array, Bounds bnds) {
			
			List<TrafficUpdate> update_list = new ArrayList<TrafficUpdate>();
			
			String bound_desc_str;
			if (bnds.boundType == Bounds.BOUND_TYPE_DATE) {
				bound_desc_str = String.format(" date '%s' to date '%s'", bnds.boundsParams.get("since"), bnds.boundsParams.get("until"));
			} else {
				bound_desc_str = String.format(" id %s", bnds.boundsParams.get("since_id"));
			}
			
			//need to concatenate the phrases into one search string, and build a map of phrases to phrase_id
			String phrase_search_str = "";
			//HashMap<String,String> phrase_id_map = new HashMap<String, String>();
			for (int phr_cnt=0; phr_cnt<phr_array.length; phr_cnt++) {
				Phrase next_phrase = phr_array[phr_cnt];
				//phrase_id_map.put(next_phrase.text, String.valueOf(next_phrase.phraseId));
				phrase_search_str += String.format("\"%s\"", plusEncode(next_phrase.text));
				if (phr_cnt < phr_array.length-1) phrase_search_str += "+OR+";
			}
			
			TLog.log(Level.FINER,String.format("Querying for terms '%s' in city '%s' from %s", phrase_search_str, cty.cityName, bound_desc_str));
			
			int page = 1;
			int attempts=1;
			final int MAX_ATTEMPTS = 20;
			
			// this loop will make 20 attempts
			while (true) {
				HashMap<String, String> params = new HashMap<String,String>();
				
				params.put("q", phrase_search_str);
				 				
				params.put("geocode", String.format("%s,%s,15km", cty.latitude, cty.longitude));
				params.put("rpp", "100");
				
				params.put("page", String.valueOf(page));

				/**
				params.put("since", date_str);
				params.put("until", date_str);
				**/
				
				params.putAll(bnds.boundsParams);
				
				List<Status> status_list = null;
				try {
					//System.err.println("Attempt #" + attempts);
					String query_result = getResultForParams(params, page);
					try {
						status_list = Status.getStatuses(query_result);
					
						Iterator<Status> statusI = status_list.iterator();
						while (statusI.hasNext()) {
							Status a_status = statusI.next();
							
							TrafficUpdate t = new TrafficUpdate(this.tSource, System.currentTimeMillis(), "traffic update");
							
							t.addData("status_id", String.valueOf(a_status.id));
							
							t.addData("city_id", String.valueOf(cty.cityId));
							t.addData("city_name", cty.cityName);
							t.addData("country_name", cty.countryName);
							
							t.addData("text", a_status.text);
							
							Date createdAt = a_status.createdAt;
							// TODO make sure this doesn't do a timeshift!!
							t.addData("mentioned_at", DiyFluSource.getDateInMySQLFormat(createdAt));
							
							String link_str = String.format("http://www.twitter.com/%s/statuses/%s",
									a_status.fromUser,
									a_status.id
							);
							// NEED TO GET THIS FROM TWITTER
							t.addData("link", link_str);
							
							long phrase_id = -1;
							for (int phr_cnt=0; phr_cnt<phr_array.length; phr_cnt++) {
								Phrase next_phrase = phr_array[phr_cnt];
								if (a_status.text.toLowerCase().contains(next_phrase.text.toLowerCase())) {
									TLog.log(Level.FINEST, 
											String.format("Classifying tweet '%s' as phrase '%s' (%s)", a_status.text, next_phrase.text, next_phrase.phraseId)
											);
									phrase_id = next_phrase.phraseId;
									break;
								}
							}
							t.addData("phrase_id", String.valueOf(phrase_id));
							
							
							t.addData("mentioner", a_status.fromUser);
							
							/** OLD - this is for when it's running synchronous - dumps all after finished **/
							//update_list.add(t);
							
							/** NEW - this pumps out updates as it's running!! **/
							try {
								AirTrafficController atc = AirTrafficController.getController();
								//if (atc!=null) {
									// i don't know why it would be null!
								atc.addUpdate(t);
								//}
							} catch (IllegalStateException iae) {
								TLog.log(Level.WARNING, iae);
								TLog.log(Level.WARNING, "DiyFlu overflowed the update queue!");
							}
							/** END NEW **/
						}
					
					} catch (TwitterException e) { // if something happens parsing results
						TLog.log(Level.WARNING, e);
					}
				} catch (FeedSourceException e) { // if something happens while issuing request
					Level use_level = (attempts > 15) ? Level.WARNING : Level.FINE;
					
					TLog.log(use_level, 
							String.format(e.getMessage() + " ('%s' in %s from %s, attempt #%d)", phrase_search_str, cty.cityName, bound_desc_str, attempts)); // add the date here***?
					
					// if you're getting 404s from Twitter, most likely you won't be able to go any
					// further back, so stop spamming them!
					if ( (attempts >= MAX_ATTEMPTS) || (e.statusCode == HttpStatus.SC_NOT_FOUND) ) {
						break;
					} else {
						attempts++;
						continue;
					}
				}
				
				int num_statuses = status_list.size();
				
				page++;

				// should this just be if numNodes==0?
				if (num_statuses < 100) {
					break;
				}
			}
			
			return update_list;
		}
		
		/**
		// do you need another function besides this one if you end up collapsing al phrases into one query?
		// YES - to process each update and assign it a phrase...
		public List<TrafficUpdate> getPhrasesForCityWithBounds(City city, Phrase[] phrases, Bounds bnds) {
			List<TrafficUpdate> update_list = new ArrayList<TrafficUpdate>();
		
			// loop through all the phrases
			for (int phrase_counter=0; phrase_counter < phrases.length; phrase_counter++) {
				List<TrafficUpdate> phrase_updates = getPhraseForCityWithBounds(city, phrases[phrase_counter], bnds);
				update_list.addAll(phrase_updates);
			}
			
			return update_list;
		}
		**/
		
		
		public List<TrafficUpdate> getPhrasesForCityForDays(City city, Phrase[] phrases, int numDaysBack) {
			
			List<TrafficUpdate> update_list = new ArrayList<TrafficUpdate>();
			
			Calendar calendar_now = GregorianCalendar.getInstance(TimeZone.getDefault());
			calendar_now.setTimeInMillis(System.currentTimeMillis());
			
			/** goes back a certain number of days... **/
			for (int day_counter=0; day_counter<numDaysBack; day_counter++) {
				
				String use_date = DiyFluSource.getDateInTwitterFormat(calendar_now.getTime());
				Bounds date_bnds = new Bounds();
				date_bnds.addDateBounds(use_date, use_date);
				
				update_list.addAll(
						getPhrasesForCityWithBounds(city, phrases, date_bnds)
						);
				calendar_now.add(Calendar.DATE, -1);
			} 
		
			return update_list;
		}
		
		@Override
		public void run() {
			getPhrasesForCityForDays(this.myCity, this.myPhrases, 60);
		}
		
	}
}
